package com.viknix.threadpool.manager.client.alertor;

import com.viknix.threadpool.manager.client.core.DynamicThreadPoolExecutor;
import com.viknix.threadpool.manager.client.config.ApplicationContextHolder;
import com.viknix.threadpool.manager.client.enums.MessageTypeEnum;
import com.viknix.threadpool.manager.client.message.SendMessageService;
import com.viknix.threadpool.manager.client.model.AlertorDTO;
import com.viknix.threadpool.manager.common.model.PoolParameterInfo;
import com.viknix.threadpool.manager.client.util.CalculateUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.BlockingQueue;

/**
 * @Author: Dongqi
 * @Date: 2021/10/22 10:24
 * @Version 1.0
 * @Description 告警器
 */
@Slf4j
public class ThreadPoolAlertor {

    /**
     * 消息发生器：
     * 用于找到所有消息处理器，委托各个消息处理器发送消息
     */
    private static final SendMessageService SEND_MESSAGE_SERVICE;

    /**
     * 报警间隔控制
     */
    private static final AlertorHandler ALERTOR_HANDLER;

    static {
        SEND_MESSAGE_SERVICE = ApplicationContextHolder.getBean("sendMessageService", SendMessageService.class);
        ALERTOR_HANDLER = ApplicationContextHolder.getBean("alertorHandler", AlertorHandler.class);
    }

    /**
     * 检查阻塞队列容量告警：
     * 有界队列会根据 capacity 告警，capacityAlarm = queueSize / (queueSize + remainingCapacity)
     * 容量告警比例 = 当前阻塞队列中的元素数量 / (当前阻塞队列中的元素数量 + 阻塞队列中剩余的容量)
     * ArrayBlockingQueue
     * LinkedBlockingDeque
     * LinkedBlockingQueue
     * 无界队列和同步队列不会进行线程池容量告警
     * DelayQueue
     * PriorityBlockingQueue
     * LinkedTransferQueue
     * SynchronousQueue
     *
     * @param threadPoolExecutor 动态线程池
     */
    public static void checkPoolCapacity(DynamicThreadPoolExecutor threadPoolExecutor) {

        // 获取告警对象
        AlertParam alertorParam = threadPoolExecutor.getAlertorParam();
        // 获取阻塞队列
        BlockingQueue blockIngQueue = threadPoolExecutor.getQueue();
        // 返回阻塞队列中的元素数量
        int queueSize = blockIngQueue.size();
        if (queueSize == 0) {
            return;
        }
        // 总容量 = 当前阻塞队列中的元素数量 + 阻塞队列中剩余的容量
        int capacity = queueSize + blockIngQueue.remainingCapacity();
        // 阻塞队列负载系数 = 阻塞队列中当前元素数量 / 阻塞队列中的总容量
        int divide = CalculateUtil.divide(queueSize, capacity);
        // 如果阻塞队列容量过载将会触发预警，发送预警信息，如果在时间阈值内已经发送过告警消息将不再发送
        if (divide > alertorParam.getCapacityAlarm() && isSendMessage(threadPoolExecutor, MessageTypeEnum.CAPACITY)) {
            // 发送告警信息
            SEND_MESSAGE_SERVICE.sendAlarmMessage(threadPoolExecutor);
        }
    }

    /**
     * 检查线程池活跃度
     * livenessAlarm = activeCount / maximumPoolSize
     *
     * @param isCore             是否是核心线程，如果是核心线程不会触发活跃度告警检测
     * @param threadPoolExecutor 动态线程池
     */
    public static void checkPoolLiveness(boolean isCore, DynamicThreadPoolExecutor threadPoolExecutor) {
        try {
            // 如果是核心线程，直接返回
            if (isCore) {
                return;
            }
            // 获取当前活跃的线程数
            int activeCount = threadPoolExecutor.getActiveCount();
            // 获取最大线程数
            int maximumPoolSize = threadPoolExecutor.getMaximumPoolSize();
            // 线程池负载 = 当前活跃的线程数/最大线程数量
            int divide = CalculateUtil.divide(activeCount, maximumPoolSize);
            // 如果线程池过载
            if (divide > threadPoolExecutor.getAlertorParam().getLivenessAlarm() && isSendMessage(threadPoolExecutor, MessageTypeEnum.LIVENESS)) {
                // 发送告警信息
                SEND_MESSAGE_SERVICE.sendAlarmMessage(threadPoolExecutor);
            }
        } catch (Exception e) {
            log.warn(">>> Failed to trigger thread pool activity warning!", e);
        }
    }

    /**
     * 检查线程池拒绝策略告警
     * Check thread pool reject policy alarm.
     *
     * @param threadPoolExecutor 动态线程池
     */
    public static void checkPoolReject(DynamicThreadPoolExecutor threadPoolExecutor) {
        if (isSendMessage(threadPoolExecutor, MessageTypeEnum.REJECT)) {
            // 发送告警信息
            SEND_MESSAGE_SERVICE.sendAlarmMessage(threadPoolExecutor);
        }

    }

    /**
     * 发送线程池配置更新消息
     * Send thread pool configuration change message.
     *
     * @param newParameter 更新之后的线程池参数
     * @param oldParameter 更新之前的线程池参数
     */
    public static void sendPoolConfigChange(PoolParameterInfo newParameter, PoolParameterInfo oldParameter) {
        SEND_MESSAGE_SERVICE.sendChangeMessage(newParameter, oldParameter);
    }

    /**
     * Is send message.
     *
     * @param threadPoolExecutor
     * @param typeEnum
     * @return
     */
    private static boolean isSendMessage(DynamicThreadPoolExecutor threadPoolExecutor, MessageTypeEnum typeEnum) {
        AlertorDTO alertorDTO = AlertorDTO.builder()
                .threadPool(threadPoolExecutor.getThreadPoolId())
                .typeEnum(typeEnum)
                .build();
        return ALERTOR_HANDLER.isSend(alertorDTO);
    }

}
